package com.xuyuan.remote;

import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class OKHttpExample {
    private static final String EXAMPLE_URL = "http://www.example.com";
    private static final String EXAMPLE_FILE_PATH = "http://www.example.com/1.jpg";

    public static void main(String[] args) throws Exception {
        // OkHttp使用建造者模式构建OkHttpClient、Request、ReqeuestBody
        // 1、GET请求
        OkHttpClient okHttpClient1 = new OkHttpClient();
        Request getRequest = new Request.Builder().url(EXAMPLE_URL).get().build();
        Call getCall = okHttpClient1.newCall(getRequest);
        Response getResponse = getCall.execute();
        if (getResponse.isSuccessful()) {
            // TODO 自定义序列化工具处理响应
            BufferedReader in = new BufferedReader(new InputStreamReader(getResponse.body().byteStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine).append("\r");
            }
            in.close();
            System.out.println("Response Body: " + response);
        }

        // 2、POST请求
        OkHttpClient okHttpClient2 = new OkHttpClient();
        RequestBody body = new FormBody.Builder().add("name", "xuyuan").build();
        Request postRequest = new Request.Builder().url(EXAMPLE_URL).post(body).build();
        Call postCall = okHttpClient2.newCall(postRequest);
        Response postResponse = postCall.execute();

        // TODO .......

        // 3、构建定制化配置的OkHttpClient对象，全局设置
        OkHttpClient okHttpClient3 = new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .addInterceptor(chain -> {
                    Request req = chain.request();
                    String url = req.url().toString();
                    System.out.println("intercept: proceed start: url" + url + ", at " + System.currentTimeMillis());
                    Response rep = chain.proceed(req);
                    System.out.println("intercept: proceed end: url" + url + ", at " + System.currentTimeMillis());
                    return rep;
                })
                .build();

        // 4、单次请求定制化配置
        Request request3 = new Request.Builder()
                .url(EXAMPLE_URL)
                .post(null)
                .addHeader("key", "value")
                .cacheControl(CacheControl.FORCE_CACHE)
                .build();


        // 5、异步请求发送
        Call call1 = okHttpClient3.newCall(request3);
        call1.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {

            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

            }
        });

        // 6、取消请求，根据业务场景使用。取消的时候，很有可能已经发出了请求，取消也只是逻辑上的取消，物理上发送了请求
        call1.cancel();

        // 7、上传文件
        OkHttpClient okHttpClient4 = new OkHttpClient();
        MediaType contentType = MediaType.parse("application/json;charset=utf-8");
        RequestBody requestBody4 = RequestBody.create(contentType, "example");
        Request request4 = new Request.Builder().post(requestBody4).url(EXAMPLE_URL).build();
        Call call4 = okHttpClient4.newCall(request4);
        Response response4 = call4.execute();

        // 8、上传Multiparty 文件
        OkHttpClient okHttpClient5 = new OkHttpClient();
        MediaType contentType5 = MediaType.parse("text/x-markdown;charset=utf-8");
        RequestBody requestBody5 = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("title", "example")
                .addFormDataPart("image", "1.jpg", RequestBody.create(contentType5, new File(EXAMPLE_FILE_PATH)))
                .build();
        Request request = new Request.Builder().url(EXAMPLE_URL).post(requestBody5).build();
        Call call = okHttpClient5.newCall(request);
    }

    private static OkHttpClient createOkHttpClient1() {
        // 1、构建简单的OkHttpClient对象，使用默认配置
        return new OkHttpClient();
    }

    private static OkHttpClient createOkHttpClient2() {
        // 2、构建复杂的的OkHttpClient对象，使用内置的Builder
        return new OkHttpClient.Builder()
                .readTimeout(5, TimeUnit.SECONDS) // 读超时时间
                .writeTimeout(3, TimeUnit.SECONDS) // 写超时时间
                .connectTimeout(1, TimeUnit.SECONDS) // 连接超时时间
                .callTimeout(5, TimeUnit.SECONDS) // 响应超时时间
                .addInterceptor(new CustomInterceptor()) // 自定义拦截器
                .cache(new Cache(new File(""), 1024 * 1024 * 100)) // 自定义缓存，CacheInterceptor会使用
                .cookieJar(new CustomCookieJar()) // 自定义Cookie，BridgeInterceptor会使用
                .followRedirects(true) // 支持RetryAndFollowUpInterceptor的重定向
                .followSslRedirects(true) // 支持SSL重定向
                .retryOnConnectionFailure(true) // 支持RetryAndFollowUpInterceptor的重试
                .connectionPool(new ConnectionPool(10, 10, TimeUnit.SECONDS))
                // 自定义ConnectionPool，ConnectInterceptor会使用自定义ConnectionPool构建连接
                .build();
    }

    private static Request createRequest1() {
        return new Request.Builder()
                .url(EXAMPLE_URL)
                .addHeader("Authorization", "Bearer access_token")
                .addHeader("Content-Type", "application/json")
                .addHeader("Accept", "application/json")
                .build();
    }

    private static Request createRequest2() {
        return new Request.Builder()
                .url(EXAMPLE_URL)
                .addHeader("Authorization", "Bearer your_access_token")
                .removeHeader("User-Agent") // 移除 User-Agent 协议头
                .build();
    }

    private static Request createRequest3() {
        Headers headers = new Headers.Builder()
                .add("Authorization", "Bearer your_access_token")
                .add("Content-Type", "application/json")
                .build();

        return new Request.Builder()
                .url(EXAMPLE_URL)
                .header("User-Agent", "OkHttp Example") // 替换或者添加单个
                .headers(headers) // 替换或者添加多个
                .build();

    }

    private static RequestBody createRequestBody1() {
        return new FormBody.Builder()
                .add("username", "john")
                .add("password", "password123")
                .build();
    }

    private static RequestBody createRequestBody2() {
        String json = "{\"name\":\"xuyuan\", \"age\":25}";
        return RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));

    }

    private static RequestBody createRequestBody3() {
        User user = new User("xuyuan", 25);
        Gson gson = new Gson();
        String json = gson.toJson(user);
        return RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));

    }

    private static RequestBody createRequestBody4() {
        File file = new File(EXAMPLE_FILE_PATH);
        return new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(),
                        RequestBody.create(file, MediaType.parse("application/octet-stream")))
                .build();
    }

    private static Request createRequest4() {
        Request getRequest = new Request.Builder()
                .url(EXAMPLE_URL)
                .get()
                .build();
        return getRequest;
    }

    private static Request createRequest4_2() {
        // 添加带请求参数的GET请求
        HttpUrl.Builder urlBuilder = HttpUrl.parse("EXAMPLE_URL").newBuilder();
        // HttpUrl.Builder urlBuilder = new HttpUrl.Builder().scheme("http").host(EXAMPLE_URL);
        // 添加查询参数
        urlBuilder.addQueryParameter("param1", "value1").addQueryParameter("param2", "value2");

        // 构建完整的URL
        HttpUrl url = urlBuilder.build();

        Request getRequest = new Request.Builder()
                .url(url)
                .get()
                .build();

        return getRequest;
    }

    private static Request createRequest4_3() {
        // 添加带路径参数的GET请求
        HttpUrl.Builder urlBuilder = HttpUrl.parse("EXAMPLE_URL").newBuilder();
        // 添加路径参数
        urlBuilder.addPathSegment("param1").addPathSegment("param2");
        // 构建完整的URL
        HttpUrl url = urlBuilder.build();

        Request getRequest = new Request.Builder()
                .url(url)
                .get()
                .build();
        return getRequest;
    }

    private static Request createRequest5() {
        Request postRequest = new Request.Builder()
                .url(EXAMPLE_URL)
                .post(createRequestBody1())
                .build();
        return postRequest;
    }

    private static Request createRequest6() {
        Headers headers = new Headers.Builder()
                .add("Authorization", "Bearer your_access_token")
                .add("Content-Type", "application/json")
                .build();

        return new Request.Builder()
                .url(EXAMPLE_URL)
                .post(createRequestBody1())
                .headers(headers)
                .cacheControl(CacheControl.FORCE_CACHE)
                .build();
    }

    private static void interceptor() throws IOException {
        // TODO OkHttp的拦截器机制采用责任链模式
        // OkHttp内置五个拦截器，构建OkHttpClient/Request设置对应参数，在执行拦截器的时候会根据设置的参数来执行
        //（1）RetryAndFollowUpInterceptor 重试和重定向,构建OkHttpClient的时候配置是否重试和重定向
        //（2）BridgeInterceptor 应用层和数据层之间的数据转换，添加固定的协议参数,构建请求对象。
        // 根据构建Request设置的请求参数，构建请求对象，包括header和body，并且默认是没有处理cookie的，需要我们在初始化OkHttpClient的时候配置自己的CookieJar。
        //（3）CacheInterceptor 请求添加缓存，快速响应，默认是没有处理Cache的，构建OkHttpClient的时候设置自己的Cache
        //（4）ConnectInterceptor 构建连接,client有默认的ConnectionPool，也可以在构建OkHttpClient的时候设置自己的ConnectionPool
        //（5）CallServerInterceptor 发起请求，处理响应，实际使用okio，而okio实际上还是用的Socket
        // (6)内置的拦截器不能满足需要也可以自己实现Interceptor
        OkHttpClient okHttpClient6 = new OkHttpClient.Builder()
                .readTimeout(5, TimeUnit.SECONDS) // 读超时时间
                .writeTimeout(3, TimeUnit.SECONDS) // 写超时时间
                .connectTimeout(1, TimeUnit.SECONDS) // 连接超时时间
                .callTimeout(5, TimeUnit.SECONDS) // 响应超时时间
                .addInterceptor(new CustomInterceptor()) // 自定义拦截器
                .cache(new Cache(new File(""), 1024 * 1024 * 100))// 自定义缓存，CacheInterceptor会使用
                .cookieJar(new CustomCookieJar()) // 自定义Cookie，BridgeInterceptor会使用
                .followRedirects(true) // 支持RetryAndFollowUpInterceptor的重定向
                .followSslRedirects(true) // 支持SSL重定向
                .retryOnConnectionFailure(true) // 支持RetryAndFollowUpInterceptor的重试
                .connectionPool(new ConnectionPool(10, 10, TimeUnit.SECONDS))
                // 自定义ConnectionPool，ConnectInterceptor会使用自定义ConnectionPool构建连接
                .build();

        Request request = new Request.Builder()
                .url(EXAMPLE_URL)
                .post(createRequestBody1())
                .cacheControl(CacheControl.FORCE_CACHE) // 请求是否采用缓存，CacheInterceptor根据配置，判断是否使用缓存
                .build();

        Call call = okHttpClient6.newCall(request);
        Response response = call.execute();
    }

    static class CustomInterceptor implements Interceptor {
        @NotNull
        @Override
        public Response intercept(@NotNull Chain chain) throws IOException {
            // TODO 自定义拦截逻辑
            Request request = chain.request();
            String url = request.url().toString();
            System.out.println("intercept: proceed start: url" + url + ", at " + System.currentTimeMillis());
            Response rep = chain.proceed(request);
            System.out.println("intercept: proceed end: url" + url + ", at " + System.currentTimeMillis());
            return rep;
        }
    }

    static class CustomCookieJar implements CookieJar {
        @NotNull
        @Override
        public List<Cookie> loadForRequest(@NotNull HttpUrl httpUrl) {
            return null;
        }

        @Override
        public void saveFromResponse(@NotNull HttpUrl httpUrl, @NotNull List<Cookie> list) {

        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class User {
        private String name;
        private int age;
    }


}